package com.jctrip.hotel.route.service;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

import com.google.common.collect.Lists;
import com.jctrip.hotel.common.enums.ErrorEnum;
import com.jctrip.hotel.common.enums.SourceEnum;
import com.jctrip.hotel.domain.dao.*;
import com.jctrip.hotel.domain.entity.*;
import com.jctrip.hotel.route.model.param.GetHotelRoomStatusParam;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.jctrip.hotel.common.client.DingTalkClient;
import com.jctrip.hotel.common.client.model.DingTalkResult;
import com.jctrip.hotel.common.client.model.DingTalkTextParam;
import com.jctrip.hotel.common.enums.OrderStatusEnum;
import com.jctrip.hotel.common.enums.RouteTypeEnum;
import com.jctrip.hotel.common.route2source.model.param.Route2SourcePostOrderParam;
import com.jctrip.hotel.common.route2source.model.result.Route2SourcePostOrderResult;
import com.jctrip.hotel.common.route2source.service.Route2SourceOrderService;
import com.jctrip.hotel.common.util.DateUtil;
import com.jctrip.hotel.common.util.JsonUtil;
import com.jctrip.hotel.common.util.RandomUtil;
import com.jctrip.hotel.route.common.ThreadLocalSession;
import com.jctrip.hotel.route.model.param.PostOrderParam;
import com.jctrip.hotel.route.model.result.PostOrderResult;

import lombok.extern.slf4j.Slf4j;

/**
 * 下单接口-服务
 */
@Slf4j
@Service
public class PostOrderService extends RouteService {

    @Autowired
    private HtPriceCalendarDao htPriceCalendarDao;

    @Autowired
    private HtPricePolicyRangeDao htPricePolicyRangeDao;

    @Autowired
    private HtRouteInfoDao htRouteInfoDao;

    @Autowired
    private HtRouteCreditLogDao htRouteCreditLogDao;

    @Autowired
    private HtOrderRouteRecordDao htOrderRouteRecordDao;

    @Autowired
    private HtOrderSourceRecordDao htOrderSourceRecordDao;

    @Autowired
    private Route2SourceOrderService route2SourceOrderService;

    @Autowired
    private MgOrderLogDao mgOrderLogDao;

    @Autowired
    private DingTalkClient dingTalkClient;

    @Autowired
    private HtRiskListDao htRiskListDao;

    @Autowired
    private HtRoomTypeMappingDao htRoomTypeMappingDao;

    public PostOrderResult execute(PostOrderParam param) {
        HtRouteInfo htRouteInfo = ThreadLocalSession.get().getRouteInfo();
        String orderId = null;
        String routeId = htRouteInfo.getRouteId();
        // 价格政策配置
        List<HtPricePolicy> htPricePolicies = getPricePolicies();
        List<String> sourceCodes = htPricePolicies.stream()
                .map(HtPricePolicy::getSourceCode)
                .collect(Collectors.toList());
        // 酒店标准信息
        HtHotelStdInfo htHotelStdInfo = getHotelStdInfo(param.getHotelId(), sourceCodes);
        if (htHotelStdInfo == null) {
            throw new RuntimeException("无效的hotelId");
        }
        // 房型标准信息
        HtRoomTypeStdInfo htRoomTypeStdInfo = getRoomTypeStdInfo(param.getHotelId(), param.getRoomTypeCode());
        if (htRoomTypeStdInfo == null) {
            throw new RuntimeException("无效的roomTypeCode");
        }
        HtHotelStdInfo finalHtHotelStdInfo = htHotelStdInfo;
        HtPricePolicy htPricePolicy = htPricePolicies.stream()
                .filter(e -> e.getSourceCode().equals(finalHtHotelStdInfo.getSourceCode()))
                .filter(e -> e.getPricePolicyCode().equals(param.getPricePolicyCode()))
                .findAny()
                .orElse(null);
        if (StringUtils.isNotBlank(htPricePolicy.getExtra())
                && Objects.nonNull(JsonUtil.toMap(htPricePolicy.getExtra()).get("mappingFlag"))) {
            transformParam(param);
            sourceCodes.add((String) JsonUtil.toMap(htPricePolicy.getExtra()).get("mappingSource"));
            htHotelStdInfo = getHotelStdInfo(param.getHotelId(), sourceCodes);
            if (htHotelStdInfo == null) {
                throw new RuntimeException("无效的hotelId");
            }
            htRoomTypeStdInfo = getRoomTypeStdInfo(param.getHotelId(), param.getRoomTypeCode());
            if (htRoomTypeStdInfo == null) {
                throw new RuntimeException("无效的roomTypeCode");
            }
            htPricePolicy = htPricePolicies.stream()
                    .filter(e -> e.getSourceCode().equals(finalHtHotelStdInfo.getSourceCode()))
                    .filter(e -> e.getPricePolicyCode().equals(param.getPricePolicyCode()))
                    .findAny()
                    .orElse(null);
        }
        if (htPricePolicy == null) {
            throw new RuntimeException("无效的pricePolicyId");
        }
        // 价格政策区间配置
        List<HtPricePolicyRange> htPricePolicyRanges = getPricePolicyRanges(htPricePolicy.getPricePolicyId());
        Date beginDate = DateUtil.parseDate(param.getArriveDate(), "yyyy-MM-dd");
        Date endDate = DateUtils.addDays(DateUtil.parseDate(param.getDepartDate(), "yyyy-MM-dd"), -1);
        // 价格日历
        List<HtPriceCalendar> htPriceCalendars =
                getPriceCalenders(param.getHotelId(), param.getRoomTypeCode(), beginDate, endDate);
        HtOrderRouteRecord htOrderRouteRecord = getOrderRouteRecord(routeId, param.getRouteOrderId());
        if (htOrderRouteRecord != null) {
            sendErrorMessage(htRouteInfo, "不能重复下单", param.getRouteOrderId(), orderId);
            throw new RuntimeException("不能重复下单:" + routeId + "," + param.getRouteOrderId());
        }
        // 账户额度检查
        if (htRouteInfo.getRouteType() == RouteTypeEnum.OUTER.getCode() &&
                htRouteInfo.getAccountCredit().subtract(param.getPaymentPrice()).compareTo(BigDecimal.valueOf(0)) < 0) {
            sendErrorMessage(htRouteInfo, "账户额度不足", param.getRouteOrderId(), orderId);
            throw new RuntimeException("账户额度不足:" + routeId + "," + param.getRouteOrderId());
        }
        orderId = RandomUtil.createOrderId();
        String bookId = RandomUtil.createBookId();
        // routeId 和 对应的价格政策配置id肯定是唯一值 作为redis的Key
        String key = htPricePolicy.getRouteId() + "_" + htPricePolicy.getPricePolicyId();
        String sourceId = RandomUtil.getRandomItem(key, htPricePolicy.getSourceIds(), ",");
        HtSourceInfo htSourceInfo = getSourceInfo(sourceId);

        try {
            /* 保存渠道订单 */
            htOrderRouteRecord = insertOrderRouteRecord(
                    param,
                    orderId,
                    htRouteInfo,
                    htPricePolicy,
                    htPriceCalendars
            );
        } catch (Exception e) {
            // 极端情况，订单号重复，retry一次
            orderId = RandomUtil.createOrderId();
            /* 保存渠道订单 */
            htOrderRouteRecord = insertOrderRouteRecord(
                    param,
                    orderId,
                    htRouteInfo,
                    htPricePolicy,
                    htPriceCalendars
            );
        }
        // 检验手机号合法程度
        validatePhone(htRouteInfo, param.getGuestTel(), param.getContactTel(), param.getRouteOrderId(), orderId);
        /* 保存供应订单 */
        HtOrderSourceRecord htOrderSourceRecord = insertOrderSourceRecord(
                param,
                orderId,
                bookId,
                htHotelStdInfo,
                htRoomTypeStdInfo,
                htSourceInfo,
                htRouteInfo,
                htPricePolicy,
                htPriceCalendars
        );
        /* 调用供应下单方法 */
        Route2SourcePostOrderParam postOrderParam = Route2SourcePostOrderParam.builder()
                .sourceCode(htHotelStdInfo.getSourceCode())
                .sourceId(htSourceInfo.getSourceId())
                .sourceAppId(htSourceInfo.getSourceAppId())
                .sourceSecret(htSourceInfo.getSourceSecret())
                .sourceExtra(htSourceInfo.getExtra())
                .hotelId(param.getHotelId())
                .roomTypeCode(param.getRoomTypeCode())
                .productCode(htPricePolicy.getProductCode())
                .roomCount(param.getRoomCount())
                .arriveDate(param.getArriveDate())
                .departDate(param.getDepartDate())
                .paymentType(htPricePolicy.getPaymentType())
                .totalPrice(param.getPaymentPrice())
                .tolerancePrice(htPricePolicy.getTolerancePrice())
                .raiseFunc(price -> getRaisedPrice(price, htPricePolicyRanges)) // 函数式编程精髓
                .bookId(bookId)
                .latestArriveTime(param.getLatestArriveTime())
                .orderNotes(param.getOrderNotes2Source())
                .guestName(param.getGuestName())
                .guestTel(StringUtils.isBlank(param.getGuestTel())
                        || (htHotelStdInfo.getSourceCode().equals(SourceEnum.HWORLD.getCode()) && StringUtils.isNotBlank(htRouteInfo.getDefaultOrderContact()))
                        ? htRouteInfo.getDefaultOrderContact() : param.getGuestTel())
                .contactName(param.getContactName())
                .contactTel(StringUtils.isBlank(param.getContactTel())
                        || (htHotelStdInfo.getSourceCode().equals(SourceEnum.HWORLD.getCode()) && StringUtils.isNotBlank(htRouteInfo.getDefaultOrderContact()))
                        ? htRouteInfo.getDefaultOrderContact() : param.getContactTel())
                .extra(htSourceInfo.getExtra())
                .breakfast(param.getBreakfast())
                .hotelExtra(htHotelStdInfo.getExtra())
                .pricePolicyExtra(htPricePolicy.getExtra())
                .build();
        try {
            Route2SourcePostOrderResult postOrderResult = route2SourceOrderService.postOrder(postOrderParam);
            htRouteInfo = htRouteInfoDao.selectById(htRouteInfo.getId());
            /* 更新渠道账户信息 */
            updateRouteInfo(htRouteInfo, param);
            /* 保存账户额度记录 */
            insertRouteCreditLog(htRouteInfo, htOrderRouteRecord);
            /* 保存订单日志 */
            insertOrderLog(htRouteInfo, htOrderRouteRecord);
            /* 更新渠道订单 */
            updateOrderRouteRecord(htOrderRouteRecord, postOrderResult);
            /* 更新供应订单 */
            updateOrderSourceRecord(htOrderSourceRecord, postOrderResult);
            return PostOrderResult.builder()
                    .orderId(orderId)
                    .orderStatus(postOrderResult.getSourceOrderStatus())
                    .routeOrderId(param.getRouteOrderId())
                    .build();
        } catch (Exception e) {
            String errorMsg = "下单失败:" + e.getMessage();
            // 繁忙需要待确认 保证后续订单状态能够成功更新
            if (ErrorEnum.APPLICATION_BUSY.getCode().equals(e.getMessage())) {
                updateOrderSourceRecord(htOrderSourceRecord, OrderStatusEnum.NOT_CONFIRMED.getCode(), errorMsg);
            } else {
                updateOrderSourceRecord(htOrderSourceRecord, OrderStatusEnum.BOOK_FAILED.getCode(), errorMsg);
            }
            sendErrorMessage(htRouteInfo, e.getMessage(), param.getRouteOrderId(), orderId);
            log.error(errorMsg, e);
            throw new RuntimeException("下单失败:" + errorMsg);
        }
    }

    private void transformParam(PostOrderParam param) {
        LambdaQueryWrapper<HtRoomTypeMapping> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(HtRoomTypeMapping::getHotelId, param.getHotelId());
        wrapper.eq(HtRoomTypeMapping::getRoomTypeCode, param.getRoomTypeCode());
        HtRoomTypeMapping htRoomTypeMapping = htRoomTypeMappingDao.selectOne(wrapper);
        if (Objects.isNull(htRoomTypeMapping)) {
            return;
        }
        param.setHotelId(htRoomTypeMapping.getMappingHotelId());
        param.setRoomTypeCode(htRoomTypeMapping.getMappingRoomTypeCode());
    }

    private void validatePhone(HtRouteInfo htRouteInfo, String guestTel, String contactTel, String routeOrderId, String orderId) {
        if (StringUtils.isNotBlank(guestTel)) {
            long count = Lists.newArrayList(guestTel.split(","))
                    .stream().filter(this::illegalPhone).count();
            if (count != 0) {
                String message = "当前订单命中嘉程风控规则，无法下单";
                sendErrorMessage(htRouteInfo, message, routeOrderId, orderId);
                throw new RuntimeException(message);
            }
        }
        if (StringUtils.isNotBlank(contactTel)) {
            long count = Lists.newArrayList(contactTel.split(","))
                    .stream().filter(this::illegalPhone).count();
            if (count != 0) {
                String message = "当前订单命中嘉程风控规则，无法下单";
                sendErrorMessage(htRouteInfo, message, routeOrderId, orderId);
                throw new RuntimeException(message);
            }
        }
    }

    private Boolean illegalPhone(String tel) {
        HtRiskList riskList =
                htRiskListDao.selectOne(new LambdaQueryWrapper<HtRiskList>()
                        .eq(HtRiskList::getMobilePhone, tel));
        return Objects.nonNull(riskList);
    }

    private List<HtPricePolicyRange> getPricePolicyRanges(String pricePolicyId) {
        LambdaQueryWrapper<HtPricePolicyRange> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(HtPricePolicyRange::getPricePolicyId, pricePolicyId);
        return htPricePolicyRangeDao.selectList(queryWrapper);
    }

    private List<HtPriceCalendar> getPriceCalenders(String hotelId, String roomTypeCode, Date beginDate, Date endDate) {
        LambdaQueryWrapper<HtPriceCalendar> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(HtPriceCalendar::getHotelId, hotelId);
        if (StringUtils.isNotBlank(roomTypeCode)) {
            queryWrapper.eq(HtPriceCalendar::getRoomTypeCode, roomTypeCode);
        }
        queryWrapper.between(HtPriceCalendar::getCalDate, beginDate, endDate);
        return htPriceCalendarDao.selectList(queryWrapper);
    }

    /**
     * 获取溢价之后的房价
     */
    private BigDecimal getRaisedPrice(BigDecimal purchasePrice, List<HtPricePolicyRange> htPricePolicyRanges) {
        HtPricePolicyRange htPricePolicyRange = htPricePolicyRanges.stream()
                .filter(e -> purchasePrice.compareTo(e.getRangePriceMin()) >= 0)
                .filter(e -> purchasePrice.compareTo(e.getRangePriceMax()) < 0)
                .findAny()
                .orElse(null);
        if (htPricePolicyRange != null) {
            return purchasePrice
                    .multiply(htPricePolicyRange.getMarkupRate().add(BigDecimal.valueOf(1)))
                    .add(htPricePolicyRange.getMarkupFixed());
        }
        // 注意这里的提示文案，不能返回溢价等内部词语
        throw new RuntimeException("下单金额验证失败，原因未知");
    }

    private void updateRouteInfo(HtRouteInfo htRouteInfo, PostOrderParam param) {
        if (htRouteInfo.getRouteType() != RouteTypeEnum.OUTER.getCode()) {
            return;
        }
        BigDecimal oldCredit = htRouteInfo.getAccountCredit();
        BigDecimal newCredit = htRouteInfo.getAccountCredit().subtract(param.getPaymentPrice());
        htRouteInfo.setAccountCredit(newCredit);
        htRouteInfoDao.updateById(htRouteInfo);
        // 发送钉钉机器人预警
        if (oldCredit.compareTo(htRouteInfo.getMinimum()) >= 0 && newCredit.compareTo(htRouteInfo.getMinimum()) < 0) {
            String content = String.format(DingTalkClient.CREDIT_ALERT_TEMPLATE, htRouteInfo.getMinimum());
            DingTalkTextParam dingTalkTextParam = DingTalkTextParam.builder()
                    .text(DingTalkTextParam.Text.builder().content(content).build())
                    .build();
            DingTalkResult dingTalkResult = dingTalkClient.sendText(htRouteInfo.getDingTalkRobot(), dingTalkTextParam);
            log.info("钉钉机器人发送消息:{}, result:{}, routeId:{}",
                    content, JsonUtil.toString(dingTalkResult), htRouteInfo.getRouteId());
        }
    }

    private void sendErrorMessage(HtRouteInfo htRouteInfo, String errorMessage, String orderRouteId, String orderId) {
        String content = String.format("%s：订单下单失败，请关注\n分销商订单编号：%s\n订单编号：%s\n异常原因：%s",
                Optional.ofNullable(htRouteInfo).orElse(new HtRouteInfo()).getRouteName(),
                orderRouteId,
                StringUtils.isNotBlank(orderId) ? orderId : "",
                errorMessage);
        DingTalkTextParam dingTalkTextParam = DingTalkTextParam.builder()
                .text(DingTalkTextParam.Text.builder().content(content).build()).build();
        dingTalkClient.sendText("35880be772bffcde36096de2d1e8c6bb105572cc03ad0b5b3d83da0a380a8301",
                dingTalkTextParam);
    }

    private void insertRouteCreditLog(HtRouteInfo htRouteInfo, HtOrderRouteRecord htOrderRouteRecord) {
        if (htRouteInfo.getRouteType() != RouteTypeEnum.OUTER.getCode()) {
            return;
        }
        HtRouteCreditLog htRouteCreditLog = new HtRouteCreditLog();
        htRouteCreditLog.setRouteId(htRouteInfo.getRouteId());
        htRouteCreditLog.setOrderId(htOrderRouteRecord.getOrderId());
        htRouteCreditLog.setCreditType(1);
        htRouteCreditLog.setAmountOfChange(htOrderRouteRecord.getPaymentPrice().multiply(new BigDecimal("-1")));
        htRouteCreditLog.setAccountCredit(htRouteInfo.getAccountCredit());
        htRouteCreditLog.setOperator("system");
        htRouteCreditLogDao.insert(htRouteCreditLog);
    }

    private void insertOrderLog(HtRouteInfo htRouteInfo, HtOrderRouteRecord htOrderRouteRecord) {
        MgOrderLog mgOrderLog = new MgOrderLog();
        mgOrderLog.setRouteId(htRouteInfo.getRouteId());
        mgOrderLog.setOrderId(htOrderRouteRecord.getOrderId());
        mgOrderLog.setInfo(String.format("%s订单下单", htOrderRouteRecord.getOrderId()));
        mgOrderLog.setOperator("system");
        mgOrderLogDao.insert(mgOrderLog);
    }

    private HtOrderRouteRecord insertOrderRouteRecord(PostOrderParam param,
                                                      String orderId,
                                                      HtRouteInfo htRouteInfo,
                                                      HtPricePolicy htPricePolicy,
                                                      List<HtPriceCalendar> htPriceCalendars) {
        HtOrderRouteRecord htOrderRouteRecord = HtOrderRouteRecord.builder()
                .orderId(orderId)
                .routeOrderId(param.getRouteOrderId())
                .routeExtendOrderId(param.getRouteExtendOrderId())
                .routeId(htRouteInfo.getRouteId())
                .routeCode(htRouteInfo.getRouteCode())
                .routeChannelCode(param.getRouteChannelCode())
                .routeHotelId(param.getRouteHotelId())
                .routeHotelName(param.getRouteHotelName())
                .routeOrderStatus(0)
                .routeRoomTypeCode(param.getRouteRoomTypeCode())
                .routeRoomTypeName(param.getRouteRoomTypeName())
                .routeRatePlanCode(param.getRouteRatePlanCode())
                .routeRatePlanName(param.getRouteRatePlanName())
                .arriveDate(DateUtil.parseDate(param.getArriveDate(), "yyyy-MM-dd"))
                .departDate(DateUtil.parseDate(param.getDepartDate(), "yyyy-MM-dd"))
                .latestArriveTime(DateUtil.parseDate(param.getLatestArriveTime(), "yyyy-MM-dd HH:mm:ss"))
                .breakfastCount(
                        param.getBreakfast() != null ? param.getBreakfast() :
                                htPricePolicy.getBreakfastCount() != null
                                        ? htPricePolicy.getBreakfastCount()
                                        : htPriceCalendars.stream().map(HtPriceCalendar::getBreakfastCount).findAny().orElse(null)
                )
                .roomCount(param.getRoomCount())
                .occupancy(param.getOccupancy())
                .stockType(param.getStockType())
                .priceType(htRouteInfo.getPriceType())
                .paymentType(htPricePolicy.getPaymentType())
                .paymentPrice(param.getPaymentPrice())
                .commissionPrice(htRouteInfo.getPriceType() == 0
                        ? param.getPaymentPrice().multiply(BigDecimal.valueOf(0.1))
                        : BigDecimal.valueOf(0))
                .receivablePrice(htRouteInfo.getPriceType() == 0
                        ? param.getPaymentPrice().multiply(BigDecimal.valueOf(0.9))
                        : param.getReceivablePrice())
                .currencyCode(
                        htPriceCalendars.stream().map(HtPriceCalendar::getCurrencyCode).findAny().orElse(null)
                )
                .contactName(param.getContactName())
                .contactTel(StringUtils.isBlank(param.getContactTel())
                        ? htRouteInfo.getDefaultOrderContact() : param.getContactTel())
                .contactEmail(param.getContactEmail())
                .guestName(param.getGuestName())
                .guestTel(StringUtils.isBlank(param.getGuestTel())
                        ? htRouteInfo.getDefaultOrderContact() : param.getGuestTel())
                .companyPayment(param.getCompanyPayment())
                .companyName(param.getCompanyName())
                .orderNotes(param.getOrderNotes())
                .paymentId(param.getPaymentId())
                .orderCreateTime(new Date())
                .build();
        if (htOrderRouteRecord.getBreakfastCount() == null) {
            htOrderRouteRecord.setBreakfastCount(param.getBreakfast());
        }
        htOrderRouteRecordDao.insert(htOrderRouteRecord);
        return htOrderRouteRecord;
    }

    private void updateOrderRouteRecord(
            HtOrderRouteRecord htOrderRouteRecord,
            Route2SourcePostOrderResult postOrderResult) {
        htOrderRouteRecord.setProfitPrice(
                htOrderRouteRecord.getReceivablePrice().subtract(postOrderResult.getPurchasePrice())
        );
        htOrderRouteRecord.setRouteOrderStatus(20);
        htOrderRouteRecord.setOrderConfirmTime(new Date());
        htOrderRouteRecordDao.updateById(htOrderRouteRecord);
    }

    private HtOrderSourceRecord insertOrderSourceRecord(PostOrderParam param,
                                                        String orderId,
                                                        String bookId,
                                                        HtHotelStdInfo htHotelStdInfo,
                                                        HtRoomTypeStdInfo htRoomTypeStdInfo,
                                                        HtSourceInfo htSourceInfo,
                                                        HtRouteInfo htRouteInfo,
                                                        HtPricePolicy htPricePolicy,
                                                        List<HtPriceCalendar> htPriceCalendars) {
        HtOrderSourceRecord htOrderSourceRecord = HtOrderSourceRecord.builder()
                .orderId(orderId)
                .sourceCode(htSourceInfo.getSourceCode())
                .sourceId(htSourceInfo.getSourceId())
                .bookId(bookId)
                .hotelId(htHotelStdInfo.getHotelId())
                .hotelName(htHotelStdInfo.getHotelName())
                .roomTypeCode(htRoomTypeStdInfo.getRoomTypeCode())
                .roomTypeName(htRoomTypeStdInfo.getRoomTypeName())
                .roomCount(param.getRoomCount())
                .sourceOrderStatus(OrderStatusEnum.NOT_CONFIRMED.getCode())
                .sourcePaymentStatus(0)
                .arriveDate(DateUtil.parseDate(param.getArriveDate(), "yyyy-MM-dd"))
                .departDate(DateUtil.parseDate(param.getDepartDate(), "yyyy-MM-dd"))
                .latestArriveTime(DateUtil.parseDate(param.getLatestArriveTime(), "yyyy-MM-dd HH:mm:ss"))
                .latestHoldTime(DateUtil.parseDate(param.getLatestArriveTime(), "yyyy-MM-dd HH:mm:ss"))
                .paymentType(htPricePolicy.getPaymentType())
                .productCode(htPricePolicy.getProductCode())
                .contactName(param.getContactName())
                .contactTel(StringUtils.isBlank(param.getContactTel())
                        ? htRouteInfo.getDefaultOrderContact() : param.getContactTel())
                .guestName(param.getGuestName())
                .guestTel(StringUtils.isBlank(param.getGuestTel())
                        ? htRouteInfo.getDefaultOrderContact() : param.getGuestTel())
                .currencyCode(
                        htPriceCalendars.stream().map(HtPriceCalendar::getCurrencyCode).findAny().orElse(null)
                )
                .refundPrice(BigDecimal.valueOf(0))
                .cancelRule(
                        htPriceCalendars.stream().findAny().map(HtPriceCalendar::getCancelRule).orElse(null)
                )
                .dailyPriceList(param.getDailyPriceList())
                .orderNotes(param.getOrderNotes2Source())
                .orderCreateTime(new Date())
                .build();
        htOrderSourceRecordDao.insert(htOrderSourceRecord);
        return htOrderSourceRecord;
    }

    private void updateOrderSourceRecord(HtOrderSourceRecord htOrderSourceRecord,
                                         Route2SourcePostOrderResult postOrderResult) {
        htOrderSourceRecord.setSourceOrderId(postOrderResult.getSourceOrderId());
        htOrderSourceRecord.setSourceOrderStatus(postOrderResult.getSourceOrderStatus());
        htOrderSourceRecord.setPurchasePrice(postOrderResult.getPurchasePrice());
        htOrderSourceRecord.setRequestBody(postOrderResult.getRequestBody());
        htOrderSourceRecord.setResponseBody(postOrderResult.getResponseBody());
        htOrderSourceRecord.setOrderModifyTime(new Date());
        htOrderSourceRecordDao.updateById(htOrderSourceRecord);
    }

    private void updateOrderSourceRecord(HtOrderSourceRecord htOrderSourceRecord,
                                         Integer sourceOrderStatus,
                                         String errorMsg) {
        htOrderSourceRecord.setSourceOrderStatus(sourceOrderStatus);
        htOrderSourceRecord.setResponseBody(errorMsg);
        htOrderSourceRecord.setOrderModifyTime(new Date());
        htOrderSourceRecordDao.updateById(htOrderSourceRecord);
    }
}
